package io.gitee.zhangbinhub.acp.boot.exceptions

import com.fasterxml.jackson.databind.ObjectMapper
import io.gitee.zhangbinhub.acp.boot.interfaces.LogAdapter
import io.gitee.zhangbinhub.acp.boot.tools.PackageTools
import io.gitee.zhangbinhub.acp.core.common.CommonTools
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.web.server.ServerWebExchange
import org.springframework.web.server.WebExceptionHandler
import reactor.core.publisher.Mono
import java.nio.charset.Charset

class ReactiveExceptionHandler(
    private val logAdapter: LogAdapter,
    private val acpWebExceptionHandler: List<AcpWebExceptionHandler>,
    private val objectMapper: ObjectMapper
) : WebExceptionHandler {
    override fun handle(exchange: ServerWebExchange, ex: Throwable): Mono<Void> = exchange.response.let { response ->
        if (response.isCommitted) {
            Mono.error(ex);
        } else {
            logAdapter.error(ex.message, ex)
            val errorResult = acpWebExceptionHandler.firstOrNull { handler -> handler.identify(ex) }?.handle(ex)
                ?: PackageTools.buildErrorResult(HttpStatus.INTERNAL_SERVER_ERROR, ex.message)
            response.headers.contentType = MediaType.APPLICATION_JSON
            response.setStatusCode(errorResult.httpStatus)
            val data = try {
                objectMapper.writeValueAsString(errorResult.errorVo)
            } catch (e: Throwable) {
                logAdapter.error(e.message, e)
                "{\"error\":\"${ex.message}\"}"
            }
            logAdapter.error("response error info [${errorResult.httpStatus}] [Content-Type:${response.headers.contentType}]: $data")
            response.writeWith(
                Mono.just(
                    response.bufferFactory().wrap(data.toByteArray(Charset.forName(CommonTools.getDefaultCharset())))
                )
            )
        }
    }
}
